/*
 *  Copyright 2007 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.webapp.struts;

import org.apache.struts.tiles.xmlDefinition.XmlDefinition;
import org.apache.struts.tiles.xmlDefinition.XmlDefinitionsSet;
import org.apache.struts.tiles.NoSuchDefinitionException;

import java.util.List;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.util.Iterator;

/**
 * <p>Used to parse config which may define containers and contained definitions
 * (see {@link com.blandware.atleap.webapp.struts.ContainerXmlParser} javadoc).
 * </p><p>Represents that definition that can be contained in some list.
 * Additional properties are defined: <b>containers</b>, which gives ability
 * to constrain lists into which definition may be put; <b>description</b> -
 * describes this definition; <b>copyable</b> - whether component can be
 * contained in different containers at the same time.
 * </p>
 * <p><a href="ContainedXmlDefinition.java.html"><i>View source</i></a></p>
 *
 * @author Roman Puchkovskiy <a href="mailto:roman.puchkovskiy@blandware.com">
 *         &lt;roman.puchkovskiy@blandware.com&gt;</a>
 * @version $Revision: 1.3 $ $Date: 2007/04/27 11:42:51 $
 * @see com.blandware.atleap.webapp.struts.ContainerXmlParser
 */
public class ContainedXmlDefinition extends XmlDefinition {

    /**
     * List of identifiers of lists to which this definition may be put.
     */
    protected List containersList = new ArrayList();

    /**
     * Whether containers list was assigned or not
     */
    protected boolean containersListAssigned = false;

    /**
     * Description
     */
    protected String description = null;

    /**
     * Whether component can be contained different containers at the same
     * time
     */
    protected boolean copyable = true;

    /**
     * Whether copyable was assigned or not
     */
    protected boolean copyableAssigned = false;

    /**
     * Returns containers list.
     *
     * @return list of identifiers of lists to which this definition may be put
     */
    public List getContainersList() {
        return containersList;
    }

    /**
     * Sets containers list.
     *
     * @param containersList list to set
     */
    public void setContainersList(List containersList) {
        this.containersList = containersList;
    }

    /**
     * Sets string property.
     *
     * @param string comma-separated string of lists identifiers
     */
    public void setContainers(String string) {
        containersList.clear();
        StringTokenizer tokenizer = new StringTokenizer(string, ",");
        while (tokenizer.hasMoreTokens()) {
            String name = tokenizer.nextToken().trim();
            containersList.add(name);
        }
        containersListAssigned = true;
    }

    /**
     * Returns description. May be null.
     *
     * @return description
     */
    public String getDescription() {
        return description;
    }

    /**
     * Sets description.
     *
     * @param description description to set
     */
    public void setDescription(String description) {
        this.description = description;
    }

    /**
     * Returns whether component can be contained different containers at the
     * same time.
     *
     * @return true if component can be copied
     */
    public boolean isCopyable() {
        return copyable;
    }

    /**
     * Sets whether component can be contained different containers at the
     * same time.
     *
     * @param copyable value to set
     */
    public void setCopyable(boolean copyable) {
        this.copyable = copyable;
        copyableAssigned = true;
    }

    /**
     * Resolve inheritance.
     * Here, firstly attributes are inherited, then lists which are containers
     * in parent are set as containers too.
     *
     * @throws org.apache.struts.tiles.NoSuchDefinitionException
     *          If an inheritance can not be solved.
     * @see org.apache.struts.tiles.xmlDefinition.XmlDefinition#resolveInheritance(org.apache.struts.tiles.xmlDefinition.XmlDefinitionsSet) 
     */
    public void resolveInheritance(XmlDefinitionsSet definitionsSet)
            throws NoSuchDefinitionException {
        super.resolveInheritance(definitionsSet);

        if (!isExtending()) {
            return;
        }

        ContainedXmlDefinition parent = (ContainedXmlDefinition) definitionsSet.getDefinition( getExtends() );

        // Iterate on each parent's attribute and, if it's container, check
        // it as container too and copy its container-specific properties
        Iterator parentAttributes = parent.getAttributes().keySet().iterator();
        while (parentAttributes.hasNext()) {
            String name = (String) parentAttributes.next();
            Object parentAttribute = parent.getAttribute(name);
            Object thisAttribute = this.getAttribute(name);
            if (parentAttribute instanceof PossibleContainer
                    && thisAttribute instanceof PossibleContainer) {
                PossibleContainer parentContainer
                        = (PossibleContainer) parentAttribute;
                PossibleContainer thisContainer
                        = (PossibleContainer) thisAttribute;
                if (parentContainer.isContainer()) {
                    // parent attribute is container, set child attribute
                    // container and copy container attributes if not defined
                    // in this container
                    thisContainer.setContainer(true);
                    if (thisContainer.getDescription() == null) {
                        thisContainer.setDescription(parentContainer.getDescription());
                    }
                }
            }
        }

        // now copy new properties from parent if not specified here
        if (!containersListAssigned) {
            setContainersList(parent.getContainersList());
        }
        if (description == null) {
            setDescription(parent.getDescription());
        }
        if (!copyableAssigned) {
            setCopyable(parent.isCopyable());
        }
    }
}
